/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#pragma once

#include <baja/app/app.hpp>
#include <baja/common.hpp>
#include <baja/observable.hpp>
#include <journal/models/interfaces.h>
#include <journal/views/interfaces.h>
#include <journal/common/settings_service.hpp>

namespace journal { namespace view {

namespace a = baja::app;
namespace b = baja;
namespace g = baja::graphics;

class photo : 
    public model::iphoto_model_observer, 
    public iphoto,
    public std::enable_shared_from_this<iphoto>
{
public:
    enum class state
    {
        uninitialized = 0,
        idle,
        moving,
        underneathSelectedPhoto,
        onTopOfSelectedPhoto,
    };

    photo(
        const std::shared_ptr<model::iphoto_model>& model,
        const std::shared_ptr<a::iinput_capture>& inputCapture
        );

    ~photo();
    static std::shared_ptr<photo> create(
        const std::shared_ptr<model::iphoto_model>& model,
        const std::shared_ptr<a::iinput_capture>& inputCapture
        );

    // iphoto
    std::shared_ptr<model::iphoto_model> model() { return _photoModel; }
    void onSiblingPhotoSelected(const b::Boolean onTopOfSelectedPhoto);
    void onSiblingPhotoDeselected();
    void beforeRemove();
    b::int32 addObserver(const std::shared_ptr<view::iphoto_observer>& observer) { return _observerSource += observer; }
    void removeObserver(const std::shared_ptr<view::iphoto_observer>& observer) { _observerSource -= observer; }
    void removeObserver(const b::int32 cookie) { _observerSource -= cookie; }
    void setAlpha(const b::float32 alpha) { _alpha = alpha; }

    // icached_render_object
    bool shouldRebuildCache() { return _rebuildCache; }
    void setRebuildCache(const bool value) { _rebuildCache = value; }
    void rebuildRenderData() { /* noop */ }
    bool renderToCache();
    void renderDynamic();
    bool getDynamicBounds(m::rectangle* bounds);

    // model::iphoto_observer
    void onPhotoMoved(
        const std::shared_ptr<model::iphoto_model>& photo,
        const b::float32& scale,
        const m::point2d& position,
        const b::float32& angle
        );

    // iinput_observer
    void onPointer(const a::pointer_args& args, void* context = nullptr);
    void onGesture(const a::gesture_args& args, void* context = nullptr);
    void onKey(const a::key_args& args, void* context = nullptr);

    // icommand_observer
    void onCommand(const view::command_id id, void* context);

private:
    void init();
    void cleanup();
    void endSelection();
    void renderPhoto(const b::float32 alpha = 1.f);
    bool hitTest(const m::point3d& point, m::point3d* photoPoint);
    void applyChange();
    void cancelChange();
    m::point2d calculatePhotoSize(const b::float32 scale);
    void raiseDirty()
    {
        _observerSource.raise(
            [&](const std::shared_ptr<view::iphoto_observer>& observer, void* context)
            {
                observer->onDirty();
            });
    }
    void update(
        const b::float32 scale,
        const m::point2d& position,
        const b::float32 angle
        );

private:

    std::weak_ptr<a::iinput_capture> _inputCapture;
    std::shared_ptr<model::iphoto_model> _photoModel;
    b::observable_source<view::iphoto_observer> _observerSource;
    b::int32 _photoObserverCookie;

    std::shared_ptr<g::itexture> _photoTexture;

    bool _rebuildCache;
    state _state;
    m::point3d _holdPoint;
    b::float32 _holdScale;
    m::point2d _holdPosition;
    b::float32 _holdAngle;
    b::float32 _newScale;
    m::point2d _newPosition;
    b::float32 _newAngle;
    b::float32 _oldScale;
    m::point2d _oldPosition;
    b::float32 _oldAngle;

    m::point2d _position;
    m::point2d _size;
    b::float32 _angle;
    b::float32 _alpha;

    b::Boolean _bringToFront;

    static b::uint32 _typeToken;
};

std::shared_ptr<iphoto> createPhoto(
    const std::shared_ptr<model::iphoto_model>& photoModel,
    const std::shared_ptr<a::iinput_capture>& inputCapture
    );

}} // namespace journal::view
